package gateway.behaviours;

import com.CharNotValidException;
import com.Constants;
import com.Event;
import com.ParamsLabel;

import gateway.GatewayAgent;
import gateway.ontology.classes.GetRemoteAgents;
import gateway.ontology.classes.RegisterAgent;
import gateway.ontology.classes.UnregisterAgent;
import gateway.ontology.classes.basic_translation.ParamNotFoundException;
import gateway.ontology.classes.basic_translation.SendMessage;
import jade.content.Concept;
import jade.content.ContentManager;
import jade.content.lang.Codec.CodecException;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.content.onto.basic.Done;
import jade.content.onto.basic.Result;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.domain.FIPAAgentManagement.FailureException;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.util.leap.ArrayList;
import jade.util.leap.List;

public class HandleGatewayRequestBehaviour extends CyclicBehaviour {
	private MessageTemplate mt;

	public HandleGatewayRequestBehaviour(MessageTemplate mt, Agent agent) {
		super(agent);
		this.mt = mt;
	}

	@Override
	public void action() {
		ACLMessage message = myAgent.receive(mt);
		if (message != null) {
			ACLMessage reply = message.createReply();
			try {
				ContentManager cm = myAgent.getContentManager();
				Action act = (Action) cm.extractContent(message);
				Concept concept = act.getAction();

				if (concept instanceof RegisterAgent) {
					reply = handleRegister(act, message);
				} else if (concept instanceof UnregisterAgent) {
					reply = handleUnregister(act, message);
				} else if (concept instanceof GetRemoteAgents) {
					reply = handleGetRemoteAgents(act, message);
				} else if (concept instanceof SendMessage) {
					// TODO remove
					long time = System.currentTimeMillis();
					reply = handleSend(act, message);
				} else {
					reply.setPerformative(ACLMessage.NOT_UNDERSTOOD);
					reply.setContent("not-understood-action");
				}
			} catch (CodecException ce) {
				ce.printStackTrace();
				reply.setPerformative(ACLMessage.NOT_UNDERSTOOD);
				reply.setContent("not-understood-codec");
				myAgent.send(reply);
			} catch (OntologyException oe) {
				oe.printStackTrace();
				reply.setPerformative(ACLMessage.NOT_UNDERSTOOD);
				reply.setContent("not-understood-ontology");
				myAgent.send(reply);
			} catch (FailureException fe) {
				reply.setPerformative(ACLMessage.FAILURE);
				String content = fe.getMessage();
				reply.setContent(content);
			}
			myAgent.send(reply);
		} else {
			block();
		}
	}
	
	protected ACLMessage handleSend(Action action, ACLMessage request)
			throws FailureException {
		GatewayAgent gateway = (GatewayAgent) myAgent;

		SendMessage message = (SendMessage) action.getAction();

		String sourceMA = gateway.getLocalMAPSAgent(message.getAgentAID());
		if (sourceMA == null)
			throw new FailureException("failure-agent-not-found");

		String targetMA = message.getMapsID();

		Event event = new Event(sourceMA, targetMA, (byte)message.getMsgName(), (byte)message.getMsgType());

		try {
			List params = message.getParameters();
			for (int i = 0; (i + 1) < params.size(); i += 2) {
				event.setParam((String) params.get(i), (String) params.get(i + 1));
			}
		} catch (CharNotValidException e) {
			e.printStackTrace();
			throw new FailureException("failure-char-not-valid");
		}
		
		if (!route(sourceMA, targetMA, event, false))
			throw new FailureException("failure-event-queued");
		return createDoneMessage("done-event-queued", action, request);
	}
	
	public boolean route(String source, String target, Event event, boolean local) {
		boolean event_queued = false;
		GatewayAgent gateway = (GatewayAgent) this.myAgent;
		if (target.equals(Constants.BROADCAST)) {
			if (!local) {
				java.util.List<String> neighbors = gateway.getNaming().getNeighborsAddress();
				for (int i = 0; i < neighbors.size(); i++) {
					try {
						String address = (String) neighbors.get(i);
						Event tmessage = event.copy();
						tmessage.setParam(ParamsLabel.EVT_ADDRESS, address);
						gateway.getChannelSender().put(tmessage);
					} catch (CharNotValidException e) {
						e.printStackTrace();
					}
				}
				event_queued = true;
			}
		} else {
			String address = gateway.getNaming().getAddress(target);
			if (!address.equals("")) {
				try {
					event.setParam(ParamsLabel.EVT_ADDRESS, address);
					gateway.getChannelSender().put(event);
					event_queued = true;
				} catch (CharNotValidException e) {
					e.printStackTrace();
				}
			}
		}
		return event_queued;
	}
	
	protected ACLMessage handleRegister(Action action, ACLMessage request)
			throws FailureException {
		GatewayAgent gateway = (GatewayAgent) myAgent;
		Concept concept = action.getAction();
		RegisterAgent registerAgent = (RegisterAgent) concept;
		if (!gateway.addLocalAgent(registerAgent.getSuffix(),
				registerAgent.getAgentAID()))
			throw new FailureException("failure-register");
		return createDoneMessage("done-register", action, request);
	}
	
	protected ACLMessage handleUnregister(Action action, ACLMessage request)
			throws FailureException {
		GatewayAgent gateway = (GatewayAgent) myAgent;
		Concept concept = action.getAction();
		UnregisterAgent unregisterAgent = (UnregisterAgent) concept;
		if (!gateway.removeAgent(unregisterAgent.getAgentAID()))
			throw new FailureException("failure-unregister");
		return createDoneMessage("done-unregister", action, request);
	}
	
	protected ACLMessage handleGetRemoteAgents(Action action, ACLMessage request)
			throws FailureException {
		GatewayAgent gateway = (GatewayAgent) myAgent;
		ACLMessage reply = request.createReply();
		reply.setPerformative(ACLMessage.INFORM);
		try {
			java.util.List<String> neighbors = gateway.getNaming().getNeighborsAgents();
			// copy Vector of neighbours to list acceptable by the ontology.
			ArrayList list = new ArrayList();
			for (int i = 0; i < neighbors.size(); i++)
				list.add(neighbors.get(i));
			Result result = new Result();
			result.setAction(action);
			result.setItems(list);
			myAgent.getContentManager().fillContent(reply, result);
		} catch (Exception e) {
			e.printStackTrace();
			throw new FailureException("failure-get-remote-agents");
		}
		return reply;
	}
	
	protected final ACLMessage createDoneMessage(String string,
			Concept concept, ACLMessage message) {
		ACLMessage reply = message.createReply();
		reply.setPerformative(ACLMessage.INFORM);
		try {
			Done done = new Done();
			done.setAction(concept);
			myAgent.getContentManager().fillContent(reply, done);
		} catch (Exception e) {
			e.printStackTrace();
			reply.setContent(string);
		}
		return reply;
	}
}
